using System.Windows.Media.Imaging;
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Xml.Serialization;
using PDOGenerator.Util;
using PDOGenerator.DbToMoreGen;

namespace PDOGenerator.MapTypes
{
    public enum ColumnTypes
    {
        Normal,
        Collection
    }

    public enum CollectionTypes
    {
        Bag,
        Map,
        List
    }

    public enum ForeignKeyTypes
    {
        KeyOnly,
        ObjectOnly,
        ObjectAndKey,
        KeyAndReadonlyObject,
        ObjectAndReadonlyKey
    }

    public class ColumnMap
    {
        internal ColumnMap()
        {
            _Table = TableMap.CurrentSerial;
        }
        public ColumnMap(TableMap t)
        {
            _Table = t;
        }

        private bool _isPresentInDatabase;

        /// <summary>
        /// Track whether an item still exists in database when re-reading database info.
        /// </summary>
        [XmlIgnore]
        public bool IsPresentInDatabase
        {
            get { return _isPresentInDatabase; }
            set { _isPresentInDatabase = value; }
        }


        private bool _ignore;

        /// <summary>
        /// Ignore database column for object mapping.  
        /// Typically used for timestamps or other fields managed completely by the database.
        /// </summary>
        public bool Ignore
        {
            get { return _ignore; }
            set { _ignore = value; }
        }

        private TableMap _Table;
        [XmlIgnore]
        public TableMap Table
        {
            get { return _Table; }
        }

        [XmlIgnore]
        public string FieldName
        {
            get
            {
                return "_" + PropertyName;
            }
        }
        [XmlIgnore]
        public string ObjectFieldName
        {
            get
            {
                return "_" + ObjectPropertyName;
            }
        }

        private bool _isUnique;
        public bool IsUnique
        {
            get { return _isUnique; }
            set { _isUnique = value; }
        }

        private bool _isSingularRelationship;
        public bool IsSingularRelationship
        {
            get { return _isSingularRelationship; }
            set { _isSingularRelationship = value; }
        }


        private string _databaseDefaultValue;
        public string DatabaseDefaultValue
        {
            get
            {
                return _databaseDefaultValue;
            }
            set
            {
                if (!string.IsNullOrEmpty(value))
                {
                    if (value[0] == 'N') // i.e. N'foo'
                    {
                        _databaseDefaultValue = value.Substring(2, value.Length - 3);
                    }
                    else if (value[0] == '(') // i.e. (123)
                    {
                        _databaseDefaultValue = value.Substring(1, value.Length - 2);
                    }
                    else
                    {
                        _databaseDefaultValue = value;
                    }
                }
                else
                {
                    _databaseDefaultValue = value;
                }


            }
        }

        private string _ColumnName;
        [Category("Database")]
        public string ColumnName
        {
            get { return _ColumnName; }
            set { _ColumnName = value; }
        }

        private string _referencedListName;
        public string ReferencedListName
        {
            get { return _referencedListName; }
            set { _referencedListName = value; }
        }

        [XmlIgnore]
        public string SafeObjectName
        {
            get
            {
                return TrimCommonSuffixes(SafeColumnName);
            }
        }

        [XmlIgnore]
        public string SafeColumnName
        {
            get
            {
                string name = ColumnName;
                int pos;

                if (name.Contains(Markers.PROXY_PKEY))
                {
                    name = name.Replace(Markers.PROXY_PKEY, "BaseKey");
                }

                pos = name.IndexOf(Markers.LOOKUP_KEY_COLUMN);
                if (pos > -1)
                {
                    name = name.Substring(0, pos);
                }

                pos = name.IndexOf(Markers.PROXY_EXTENDER);
                if (pos > -1)
                {
                    name = name.Substring(0, pos);
                }

                return name;
            }

        }


        //public string ReferencedListName
        //{
        //    get { return _ColumnName; } // CX TEMP
        //}


        private string _PropertyName;
        /// <summary>
        /// Name used when this column is referred to as a simple data value.
        /// </summary>
        [Category("Class")]
        public string PropertyName
        {
            get { return _PropertyName; }
            set { _PropertyName = value; }
        }

        private string _objectPropertyName;
        /// <summary>
        /// Name used when this column is referred to as an object.
        /// </summary>
        public string ObjectPropertyName
        {
            get { return _objectPropertyName; }
            set { _objectPropertyName = value; }
        }



        private static string _uriRootPath = "pack://application:,,,/Icons/";
        private static Dictionary<string, BitmapImage> _objectPropertyImages;

        private static BitmapImage GetObjectPropertyImage(string key)
        {
            if (null == _objectPropertyImages)
            {
                _objectPropertyImages = new Dictionary<string, BitmapImage>();
            }

            if (!_objectPropertyImages.ContainsKey(key))
            {
                _objectPropertyImages.Add(key, new BitmapImage(new Uri(_uriRootPath + key)));
            }
            return _objectPropertyImages[key];
        }

        [XmlIgnore]
        public BitmapImage ObjectPropertyImage
        {
            get
            {
                if (IsPK)
                {
                    return GetObjectPropertyImage("Key.png");
                }
                if (IsCollection)
                {
                    return GetObjectPropertyImage("VertArrowsGreen.png");
                }
                if (IsFK)
                {
                    return GetObjectPropertyImage("InsertRow.png");
                }
                else
                {
                    return GetObjectPropertyImage("TableColumn.png");
                }
            }
        }

        private string _dataType;
        public string DataType
        {
            get { return _dataType; }
            set { _dataType = value; }
        }

        private string _DBType;
        public string DBType
        {
            get { return _DBType; }
            set { _DBType = value; }
        }

        private bool _IsPK;
        public bool IsPK
        {
            get { return _IsPK; }
            set { _IsPK = value; }
        }

        private bool _Nullable;
        public bool Nullable
        {
            get { return _Nullable; }
            set { _Nullable = value; }
        }

        [XmlIgnore]
        public bool IsString
        {
            get { return DataType == "string"; }
        }

        private bool _ReadOnly;
        public bool ReadOnly
        {
            get
            {
                if (Table.ReadOnly)
                    return true;

                return _ReadOnly;
            }
            set { _ReadOnly = value; }
        }

        private bool _Identity;
        public bool Identity
        {
            get { return _Identity; }
            set { _Identity = value; }
        }

        private bool _Calculated;
        public bool Calculated
        {
            get { return _Calculated; }
            set { _Calculated = value; }
        }

        private bool _isCollection;
        public bool IsCollection
        {
            get { return _isCollection; }
            set { _isCollection = value; }
        }

        private CollectionTypes _CollectionType;
        public CollectionTypes CollectionType
        {
            get { return _CollectionType; }
            set { _CollectionType = value; }
        }

        private string _CollectionColumn;
        public string CollectionColumn
        {
            get { return _CollectionColumn; }
            set { _CollectionColumn = value; }
        }

        private string _collectionTableMoniker;
        public string CollectionTableMoniker
        {
            get { return _collectionTableMoniker; }
            set
            {
                if (!string.IsNullOrEmpty(_collectionTableMoniker) && !_collectionTableMoniker.Equals(value))
                {
                    // This column has a non-unique mapping.
                    // It can't be used to generate foreign key obect realtionships
                    // since it points to multiple, different foreign tables.
                    IsFK = false;
                }
                _collectionTableMoniker = value;
            }
        }

        private string _IndexColumn;
        public string IndexColumn
        {
            get { return _IndexColumn; }
            set { _IndexColumn = value; }
        }

        [XmlIgnore]
        public bool IsData
        {
            get { return !_isCollection; }
        }

        private bool _IsFK;
        public bool IsFK
        {
            get { return _IsFK; }
            set { _IsFK = value; }
        }

        private ForeignKeyTypes _FKType = ForeignKeyTypes.ObjectAndKey;
        public ForeignKeyTypes ForeignKeyType
        {
            get { return _FKType; }
            set { _FKType = value; }
        }

        private string _Sequence;
        public string Sequence
        {
            get { return _Sequence; }
            set { _Sequence = value; }
        }


        [XmlIgnore]
        public string ForeignType
        {
            get
            {
                if (IsFK)
                {
                    return ForeignTable.ClassName;
                }
                else
                {
                    return null;
                }
            }
        }

        [XmlIgnore]
        public TableMap ForeignTable
        {
            get
            {
                try
                {
                    return Table.Project.AllTables[CollectionTableMoniker];
                }
                catch
                {
                    return null;
                }
            }
        }

        #region Inferred column properties

        // These properties are inferred from other fundamental column properties.
        // Values are different depending on source database type.

        private string _setFunctionName;

        [XmlIgnore]
        public string SetFunctionName
        {
            get { return _setFunctionName; }
            set { _setFunctionName = value; }
        }


        private string _dbConversionFunctionName;

        [XmlIgnore]
        public string DbConversionFunctionName
        {
            get { return _dbConversionFunctionName; }
            set { _dbConversionFunctionName = value; }
        }

        private string _entityDefaultValue;

        [XmlIgnore]
        public string EntityDefaultValue
        {
            get { return _entityDefaultValue; }
            set { _entityDefaultValue = value; }
        }

        private int _columnScale;

        public int ColumnScale
        {
            get { return _columnScale; }
            set { _columnScale = value; }
        }

        private int _columnPrecision;

        public int ColumnPrecision
        {
            get { return _columnPrecision; }
            set { _columnPrecision = value; }
        }

        [XmlIgnore]
        public string ColumnPrecisionString
        {
            get
            {
                if (ColumnPrecision == -1) // SQL server returns a -1 for MAX - as in VARCHAR(MAX)
                {
                    return "MAX";
                }
                else
                {
                    return ColumnPrecision.ToString();
                }

            }
        }

        private int _maxStringLength;

        [XmlIgnore]
        public int MaxStringLength
        {
            get { return _maxStringLength; }
            set { _maxStringLength = value; }
        }

        [XmlIgnore]
        public string ColumnSelectName
        {
            get
            {
                string sqlSafeName;
                string name = ColumnName;
                string safeName = SafeColumnName;

                if (!name.Equals(safeName))
                {
                    sqlSafeName = "[" + name + "] AS [" + safeName + "]";
                }
                else
                {
                    sqlSafeName = "[" + name + "]";
                }

                return sqlSafeName;
            }
        }

        #endregion




        public override string ToString()
        {
            return PropertyName;
        }

        private string[] _commonSuffixes = { "GUID", "ID" };

        private bool MatchesCommonSuffixes(string columnName)
        {
            bool matched = false;
            foreach (string suffix in _commonSuffixes)
            {
                if (matched)
                    break;

                if (columnName.Length < suffix.Length)
                    continue;

                if (columnName.Equals(suffix, StringComparison.OrdinalIgnoreCase) || // exact match
                    columnName.LastIndexOf(suffix, StringComparison.OrdinalIgnoreCase) ==
                        (columnName.Length - suffix.Length)) // match rightmost characters
                {
                    matched = true;
                }
            }
            return matched;
        }

        private string TrimCommonSuffixes(string name)
        {
            foreach (string suffix in _commonSuffixes)
            {
                // If provided name is nothing but a suffix, just return that.
                // Can't remove the suffix if there'd be nothing left!
                if (name.Equals(suffix, StringComparison.InvariantCultureIgnoreCase))
                {
                    return name;
                }


                string opn = name;
                int pos;
                pos = name.LastIndexOf(suffix, StringComparison.InvariantCultureIgnoreCase);
                if (pos > -1)
                {
                    opn = opn.Substring(0, name.Length - suffix.Length);

                    // Trim off trailing "_"
                    if (opn.EndsWith("_"))
                    {
                        opn = opn.Substring(0, opn.Length - 1);
                    }
                    return opn;
                }
            }
            return name;
        }


        public void SetObjectNames(TableMap t)
        {
            string propertyName = "**SetObjectNames**";

            if (_ColumnName != Table.ClassName)
            {
                propertyName = FixCaseName();
            }
            else
            {
                if (DataType == "string")
                {
                    propertyName = FixCaseName() + "Text";
                }
                else
                {
                    propertyName = FixCaseName() + "Value";
                }
            }

            PropertyName = propertyName;


            string opn = FromDb.SafeClassName(propertyName);

            if (IsFK && !MatchesCommonSuffixes(opn))
            {
                ObjectPropertyName = ForeignType;
            }
            else
            {
                ObjectPropertyName = TrimCommonSuffixes(opn);
            }

            // Check for self-referencing child objects
            if (ObjectFieldName == t.TableName)
            {
                ObjectPropertyName = TableMap.PARENT_PREFIX + ObjectFieldName;
            }

        }


        private string FixCaseName()
        {
            if (!Table.Project.Database.FixCase)
                return ColumnName;

            char[] t = ColumnName.ToLower().ToCharArray();

            for (int i = 0; i < t.Length; ++i)
            {
                if (t[i] == '_')
                {
                    if ((i + 1) < t.Length)
                        t[i + 1] = Char.ToUpper(t[i + 1]);
                    continue;
                }
                if (i == 0)
                    t[i] = char.ToUpper(t[0]);
            }

            return new String(t);
        }



    }
}
